#ifndef INPUT_HPP
#define INPUT_HPP

/********************************************************************
	created:	2011/01/15
	filename: 	E:\C++\Red\src\Graphics\Input.hpp
	author:		Red
	
	purpose:	Input class for input/events handling
*********************************************************************/


#include "Window.hpp"
#include "Utils/Math/Vector2.hpp"
#include "Keys.hpp"

namespace red{

	/// Input Type, Mouse or Keyboard only
	enum InputType{
		IT_Mouse,
		IT_Keyboard
	};

	/// Structure used to store binding possibilities
	struct InputBind{
		InputType mInputType;				/// Input Type (Mouse or Keyboard)
		sf::Event::EventType mEventType;	/// Event Type
		Key mKeyCode;						/// If keyboard, its KeyCode
		MouseButton mMouseButton;			/// If mouse, its ButtonCode
	};
	
	/// Input interface. Abstract class.
	/// Is inherited by the Window Class for simple use
	class REDAPI Input{
	public:
		Input();

		/// Initialize the inputs with the reference of the main window
		void Initialize(Window &pWindow);

		/// Retrieve events from the Window
		/// @return : True if an event has been retrieved
		bool GetEvents();

		/// Return the last Event retrieved
		const sf::Event& GetLastEvent() const { return mEvents; }

		/// Add a Mouse binding to the map (TODO : Settings parser for key binding parser)
		void AddMouseBinding(const std::string &pName, sf::Event::EventType pEvent, MouseButton pButton);

		/// Add a Keyboard binding to the map (TODO : Settings parser for key binding parser)
		void AddKeyBinding(const std::string &pName, sf::Event::EventType pEvent, Key pKey);

		// Returns the Mouse Movement since last frame
		//const f32 GetMouseMovementX() const { return mData.Mouse.movement.x; }
		//const f32 GetMouseMovementY() const { return mData.Mouse.movement.y; }

		/// Returns the horizontal mouse position
		const s32 GetMouseX() const { return mInputs->GetMouseX(); }
		
		/// Returns the vertical mouse position
		const s32 GetMouseY() const { return mInputs->GetMouseY(); }

		// Keyboard acquisition
			/// Returns true whenever this key is down
			bool IsKeyDown(Key pKey);

			/// Returns true if a a key has just been hit
			bool IsKeyHit(Key pKey);

			/// Returns true if the key has just been released
			bool IsKeyUp(Key pKey);
		
		// Mouse Buttons acquisition
			/// Returns true whenever this mouse button is down
			bool IsMouseDown(MouseButton button);

			/// Returns true if the mouse button has just been pressed
			bool IsMouseUp(MouseButton button);

			/// Returns true if the mouse button has just been releases
			bool IsMouseHit(MouseButton button);

		/// Returns true if the window gets more events. Call it in a while() loop
		//virtual bool GetEvent() = 0;

		/// Returns current event type
		sf::Event::EventType GetEventType() { return mEvents.Type; }

		/// Return true if wheel has moved up since last frame
		bool IsWheelUp();

		/// Return true if wheel has moved down since last frame
		bool IsWheelDown();

	protected:
		/// Map between an action (string) and a particular key or button
		std::map<std::string, InputBind> mInputBinds;
		
		Window* mWindow;			///< Current Window
		
		const sf::Input* mInputs;	///< Inputs of the current Window
		sf::Event mEvents;			///< General used event

		bool mInputInitialized;		///< Inputs initialized or not (true after first test)
		s32 mWheelPos;				///< Mouse Wheel current position
	};
	
	
	inline bool Input::IsKeyDown(Key pCode){
		if(mInputs)
			return mInputs->IsKeyDown((sf::Key::Code)pCode);
		throw Exception("InputManager has been called but has not been initialized with SetInputs()");
	}

	inline bool Input::IsKeyHit(Key pCode){
		return (mEvents.Type == sf::Event::KeyPressed && mEvents.Key.Code == (sf::Key::Code)pCode);
	}

	inline bool Input::IsKeyUp(Key pCode){
		return (mEvents.Type == sf::Event::KeyReleased && mEvents.Key.Code == (sf::Key::Code)pCode);
	}

	inline bool Input::IsMouseDown(MouseButton pButton){
		if(mInputs)
			return mInputs->IsMouseButtonDown((sf::Mouse::Button)pButton);
		throw Exception("InputManager has been called but has not been initialized with SetInputs()");
	}

	inline bool Input::IsMouseHit(MouseButton pCode){
		return (mEvents.Type == sf::Event::MouseButtonPressed && mEvents.MouseButton.Button == (sf::Mouse::Button)pCode);
	}

	inline bool Input::IsMouseUp(MouseButton pCode){
		return (mEvents.Type == sf::Event::MouseButtonReleased && mEvents.MouseButton.Button == (sf::Mouse::Button)pCode);
	}

	inline bool Input::IsWheelDown(){
		return (mEvents.Type == sf::Event::MouseWheelMoved && mEvents.MouseWheel.Delta < 0);
	}

	inline bool Input::IsWheelUp(){
		return (mEvents.Type == sf::Event::MouseWheelMoved && mEvents.MouseWheel.Delta > 0);
	}
}
#endif
